package Mode;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 阻塞队列:
 * 1. 线程安全
 * 2. 会产生阻塞效果
 *      1) 如果队列为空, 尝试出队列, 会出现阻塞, 直到队列不为空为止
 *      2) 如果队列为满, 尝试入队列, 也会出现阻塞, 直到队列不为满
 *
 * 通过阻塞队列, 能实现"生产者消费者模型", 这是一个非常有用的多线程开发手段, 尤其是服务器开发的过程中
 *
 * 这里假设有两个服务器A和B进行交互
 * 如果他们直接互相交互, 那么耦合性太高, 依赖性太强, 一个改了另一个就也要改
 * 所以在他们两个服务器之间设置一个阻塞队列, 来作为交易场所, 充分解耦合
 *
 * 另外还有一个优点:能够削峰填谷
 * 削峰: A服务器请求量突然暴涨, 阻塞队列扩大即可, 服务器B保持原来的进度即可(阻塞队列没啥计算量, 但是)
 * 填谷: B任然按照原来的频率处理阻塞队列积压的数据
 * 就像三峡大坝一样,到了雨季,水流量大,关闸蓄水;到了旱季,开闸放水
 *
 * 这个阻塞队列已经不是一个普通的数据结构了. 它是一个专门的服务器程序, 并且它还会提供更多的功能, 比如数据持久化存储, 多个数据通道等
 */

/**
 * 自己实现一个阻塞队列
 * 利用数组写一个循环队列
 */


class MyBlockingQueue{
    private int[] elem = new int[1000];
    private int usedSize = 0;//数组有效数字长度, 用来判满和判空
    private int head = 0;//头指针
    private int tail = 0;//尾指针
    private Object locker = new Object();

    public void put(int k) throws InterruptedException {
        //因为这是一连串的读与写操作,所以需要加锁
        synchronized (locker){
            if (usedSize == elem.length){
                //满了
                locker.wait();
            }
            elem[tail++] = k;
            usedSize++;
            if (tail == elem.length){
                //tail到末尾了
                tail = 0;
            }
            //不空了,通知可以取了
            locker.notify();
        }
    }

    public Integer take() throws InterruptedException {
        synchronized (locker){
            if (usedSize == 0){
                //空了
                locker.wait();
            }
            int k = elem[head];
            head++;
            usedSize--;
            if (head == elem.length){
                //head到头了
                head = 0;
            }
            //取完了,数组不满了,通知它可以放了
            locker.notify();
            return k;
        }
    }
}

public class Test2 {
    //实现一个简单的生产者消费者模型

    private static MyBlockingQueue queue = new MyBlockingQueue();

    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            int num = 0;
            while (true){
                try {
                    System.out.println("生产了:" + num);
                    queue.put(num);
                    num++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();

        Thread customer = new Thread(() -> {
            while (true){
                try {
                    int num = queue.take();
                    System.out.println("消费了" + num);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        customer.start();
    }
}
